module top #(
  parameter UART_CLK_DIV = 1736
)(
	input	sysclk_p,
	input	sysclk_n,

        input sys_rst,
	input	cpu_reset,

	output	uart_cts,
	input	uart_rts,
	output	uart_rx,
	input	uart_tx,

	//output	gpio_led_0,
	//output	gpio_led_1,
	//output	gpio_led_2,
	//output	gpio_led_3,
	//output	gpio_led_4,
	//output	gpio_led_5,
	//output	gpio_led_6,
	//output	gpio_led_7,

	//input	PHY_COL,
	//input	PHY_CRS,
	output	phy_mdc,
	inout	phy_mdio,
	output	phy_reset, // low active
	//input	PHY_RXCLK,
	//input	PHY_RXCTL_RXDV,
	//input	PHY_RXD0,
	//input	PHY_RXD1,
	//input	PHY_RXD2,
	//input	PHY_RXD3,
	//input	PHY_RXER,
	//input	PHY_TXCLK,
	//output	PHY_TXCTL_TXEN,
	//output	PHY_TXD0,
	//output	PHY_TXD1,
	//output	PHY_TXD2,
	//output	PHY_TXD3,
	//output	PHY_TXER,

        output  [15:0]  ddr3_addr,
        output  [ 2:0]  ddr3_ba,
        output          ddr3_cas_n,
        output          ddr3_cke,
        output          ddr3_ck_n,
        output          ddr3_ck_p,
        output  [ 7:0]  ddr3_dm,
        inout   [63:0]  ddr3_dq,
        inout   [ 7:0]  ddr3_dqs_n,
        inout   [ 7:0]  ddr3_dqs_p,
        output          ddr3_odt,
        output          ddr3_ras_n,
        output          ddr3_reset_n,
        output          ddr3_cs_n,
        output          ddr3_we_n,
        output          ddr3_phy_init_done
);

wire PHY_COL = 1'b0;
wire PHY_CRS = 1'b0;
wire PHY_RXCLK = 1'b0;
wire PHY_RXCTL_RXDV = 1'b0;
wire PHY_RXD0 = 1'b0;
wire PHY_RXD1 = 1'b0;
wire PHY_RXD2 = 1'b0;
wire PHY_RXD3 = 1'b0;
wire PHY_RXER = 1'b0;
wire PHY_TXCLK = 1'b0;

wire PHY_TXCTL_TXEN;
wire PHY_TXD0;
wire PHY_TXD1;
wire PHY_TXD2;
wire PHY_TXD3;
wire PHY_TXER;

wire	[3:0]	mtxd_pad_o;
wire	[3:0]	mrxd_pad_i;
wire	md_pad_i, md_pad_o, md_padoe_o;

wire            ddr3_i_wb_cyc;
wire            ddr3_i_wb_stb;
wire            ddr3_i_wb_we ;
wire    [ 2:0]  ddr3_i_wb_cti;
wire    [ 1:0]  ddr3_i_wb_bte;
wire    [30:2]  ddr3_i_wb_adr;
wire    [ 3:0]  ddr3_i_wb_sel;
wire    [31:0]  ddr3_i_wb_dat;
wire            ddr3_o_wb_ack;
wire    [31:0]  ddr3_o_wb_dat;

wire            mig_app_wdf_wren;
wire    [511:0] mig_app_wdf_data;
wire    [ 63:0] mig_app_wdf_mask;
wire            mig_app_wdf_end;
wire    [ 27:0] mig_app_addr;
wire    [  2:0] mig_app_cmd;
wire            mig_app_en;
wire            mig_app_rdy;
wire            mig_app_wdf_rdy;
wire    [511:0] mig_app_rd_data;
wire            mig_app_rd_data_end;
wire            mig_app_rd_data_valid;

wire            sys_clk;

`ifdef USE_GENERIC_LIB
  `define IBUFGDS generic_ibufds
  `define IOBUF generic_iobuf
  `define IOBUFDS generic_iobufds
  `define OBUFDS generic_obufds
`else
  `define IBUFGDS IBUFGDS
  `define IOBUF IOBUF
  `define IOBUFDS IOBUFDS
  `define OBUFDS OBUFDS
`endif

`ifndef ENABLE_DDR3
`IBUFGDS u_IBUFGDS_sysclk(
        .I (sysclk_p),
        .IB(sysclk_n),
        .O (sys_clk)
);
`endif

`IOBUF u_IOBUF_mdio(
	.O (md_pad_i),
	.IO(phy_mdio),
	.I (md_pad_o),
	.T (~md_padoe_o)
);

// Ethernet MAC
`ifdef ENABLE_ETHMAC

// TODO broken

assign	{PHY_TXD3, PHY_TXD2, PHY_TXD1, PHY_TXD0} = mtxd_pad_o;

assign	mrxd_pad_i = {PHY_RXD3, PHY_RXD2, PHY_RXD1, PHY_RXD0};

assign	phy_reset = ~sys_rst;

ethmac u_ethmac (
    .wb_clk_i                   ( sys_clk                ),
    .wb_rst_i                   ( sys_rst                ),

    // WISHBONE slave
    .wb_adr_i                   ( ems_wb_adr             ),
    .wb_sel_i                   ( ems_wb_sel             ),
    .wb_we_i                    ( ems_wb_we              ),
    .wb_cyc_i                   ( ems_wb_cyc             ),
    .wb_stb_i                   ( ems_wb_stb             ),
    .wb_ack_o                   ( ems_wb_ack             ),
    .wb_dat_i                   ( ems_wb_wdat            ),
    .wb_dat_o                   ( ems_wb_rdat            ),

    // WISHBONE master
    .m_wb_adr_o                 ( emm_wb_adr             ),
    .m_wb_sel_o                 ( emm_wb_sel             ),
    .m_wb_we_o                  ( emm_wb_we              ),
    .m_wb_dat_i                 ( emm_wb_rdat            ),
    .m_wb_dat_o                 ( emm_wb_wdat            ),
    .m_wb_cyc_o                 ( emm_wb_cyc             ),
    .m_wb_stb_o                 ( emm_wb_stb             ),
    .m_wb_ack_i                 ( emm_wb_ack             ),

    // MAC to PHY I/F
    .mtx_clk_pad_i              ( PHY_TXCLK              ),
    .mtxd_pad_o                 ( mtxd_pad_o             ),
    .mtxen_pad_o                ( PHY_TXCTL_TXEN         ),
    .mtxerr_pad_o               ( PHY_TXER               ),
    .mrx_clk_pad_i              ( PHY_RXCLK              ),
    .mrxd_pad_i                 ( mrxd_pad_i             ),
    .mrxdv_pad_i                ( PHY_RXCTL_RXDV         ),
    .mrxerr_pad_i               ( PHY_RXER               ),
    .mcoll_pad_i                ( PHY_COL                ),
    .mcrs_pad_i                 ( PHY_CRS                ),
    .md_pad_i                   ( md_pad_i               ),
    .mdc_pad_o                  ( phy_mdc                ),
    .md_pad_o                   ( md_pad_o               ),
    .md_padoe_o                 ( md_padoe_o             ),

    // Interrupt
    .int_o                      ( ethmac_irq             )
);

ethmac_wb #(
    .WB_DWIDTH              ( WB_DWIDTH   ),
    .WB_SWIDTH              ( WB_SWIDTH   )
    )
u_ethmac_wb (
    // Wishbone arbiter side
    .o_m_wb_adr             ( m0_wb_adr   ),
    .o_m_wb_sel             ( m0_wb_sel   ),
    .o_m_wb_we              ( m0_wb_we    ),
    .i_m_wb_rdat            ( m0_wb_dat_r ),
    .o_m_wb_wdat            ( m0_wb_dat_w ),
    .o_m_wb_cyc             ( m0_wb_cyc   ),
    .o_m_wb_stb             ( m0_wb_stb   ),
    .i_m_wb_ack             ( m0_wb_ack   ),

    // Wishbone arbiter side
    .i_s_wb_adr             ( s3_wb_adr   ),
    .i_s_wb_sel             ( s3_wb_sel   ),
    .i_s_wb_we              ( s3_wb_we    ),
    .i_s_wb_cyc             ( s3_wb_cyc   ),
    .i_s_wb_stb             ( s3_wb_stb   ),
    .o_s_wb_ack             ( s3_wb_ack   ),
    .i_s_wb_wdat            ( s3_wb_dat_w ),
    .o_s_wb_rdat            ( s3_wb_dat_r ),

    // Ethmac side
    .i_m_wb_adr             ( emm_wb_adr  ),
    .i_m_wb_sel             ( emm_wb_sel  ),
    .i_m_wb_we              ( emm_wb_we   ),
    .o_m_wb_rdat            ( emm_wb_rdat ),
    .i_m_wb_wdat            ( emm_wb_wdat ),
    .i_m_wb_cyc             ( emm_wb_cyc  ),
    .i_m_wb_stb             ( emm_wb_stb  ),
    .o_m_wb_ack             ( emm_wb_ack  ),

    // Ethmac side
    .o_s_wb_adr             ( ems_wb_adr  ),
    .o_s_wb_sel             ( ems_wb_sel  ),
    .o_s_wb_we              ( ems_wb_we   ),
    .i_s_wb_rdat            ( ems_wb_rdat ),
    .o_s_wb_wdat            ( ems_wb_wdat ),
    .o_s_wb_cyc             ( ems_wb_cyc  ),
    .o_s_wb_stb             ( ems_wb_stb  ),
    .i_s_wb_ack             ( ems_wb_ack  )
);

`else // ENABLE_ETHMAC not defined

assign md_pad_o = 1'b0;
assign md_padoe_o = 1'b0;

assign phy_mdc = 1'b0;
assign phy_reset = 1'b1; // low active
assign PHY_TXCTL_TXEN = 1'b0;
assign PHY_TXD0 = 1'b0;
assign PHY_TXD1 = 1'b0;
assign PHY_TXD2 = 1'b0;
assign PHY_TXD3 = 1'b0;
assign PHY_TXER = 1'b0;

`endif // ENABLE_ETHMAC

wire        instr_read;
wire [31:2] instr_addr;
wire        instr_ack;
wire [31:0] instr_data;

wb_ddr3_bridge u_wb_ddr3_bridge(
        .i_clk                  (sys_clk),
        .i_rst                  (sys_rst | cpu_reset),
        .i_wb_adr               (ddr3_i_wb_adr),
        .i_wb_sel               (ddr3_i_wb_sel),
        .i_wb_we                (ddr3_i_wb_we ),
        .o_wb_dat               (ddr3_o_wb_dat),
        .i_wb_dat               (ddr3_i_wb_dat),
        .i_wb_cyc               (ddr3_i_wb_cyc),
        .i_wb_stb               (ddr3_i_wb_stb),
        .o_wb_ack               (ddr3_o_wb_ack),
        .i_instr_read(instr_read),
        .i_instr_addr(instr_addr[30:2]),
        .o_instr_ack(instr_ack),
        .o_instr_data(instr_data),
        .o_app_addr             (mig_app_addr),
        .o_app_cmd              (mig_app_cmd),
        .o_app_en               (mig_app_en),
        .i_app_rdy              (mig_app_rdy),
        .i_app_rd_data          (mig_app_rd_data),
        .i_app_rd_data_end      (mig_app_rd_data_end),
        .i_app_rd_data_valid    (mig_app_rd_data_valid),
        .o_app_wdf_data         (mig_app_wdf_data),
        .o_app_wdf_end          (mig_app_wdf_end),
        .o_app_wdf_mask         (mig_app_wdf_mask),
        .i_app_wdf_rdy          (mig_app_wdf_rdy),
        .o_app_wdf_wren         (mig_app_wdf_wren)
);

// DDR3 SDRAM
`ifdef ENABLE_DDR3
mig_7series_v1_9 // TODO rename to reflect current version
u_ddr3(
        // inout
        .ddr3_dq(ddr3_dq),
        .ddr3_dqs_n(ddr3_dqs_n),
        .ddr3_dqs_p(ddr3_dqs_p),
        // output
        .ddr3_addr(ddr3_addr[13:0]),
        .ddr3_ba(ddr3_ba),
        .ddr3_ras_n(ddr3_ras_n),
        .ddr3_cas_n(ddr3_cas_n),
        .ddr3_we_n(ddr3_we_n),
        .ddr3_reset_n(ddr3_reset_n),
        .ddr3_ck_p(ddr3_ck_p),
        .ddr3_ck_n(ddr3_ck_n),
        .ddr3_cke(ddr3_cke),
        .ddr3_cs_n(ddr3_cs_n),
        .ddr3_dm(ddr3_dm),
        .ddr3_odt(ddr3_odt),
        // input
        .sys_clk_p(sysclk_p),
        .sys_clk_n(sysclk_n),
        // user interface signals
        // input
        .app_addr(mig_app_addr),
        .app_cmd(mig_app_cmd),
        .app_en(mig_app_en),
        .app_wdf_data(mig_app_wdf_data),
        .app_wdf_end(mig_app_wdf_end),
        .app_wdf_mask(mig_app_wdf_mask),
        .app_wdf_wren(mig_app_wdf_wren),
        // output
        .app_rd_data(mig_app_rd_data),
        .app_rd_data_end(mig_app_rd_data_end),
        .app_rd_data_valid(mig_app_rd_data_valid),
        .app_rdy(mig_app_rdy),
        .app_wdf_rdy(mig_app_wdf_rdy),
        // input
        .app_sr_req(1'b0), // reserved, should be 0
        // output
        .app_sr_active(), // reserved
        // input
        .app_ref_req(1'b0), // refresh, active-high
        // output
        .app_ref_ack(),
        // input
        .app_zq_req(1'b0), // ZQ calibration, active-high
        // output
        .app_zq_ack(),
        .ui_clk(sys_clk),
        .ui_clk_sync_rst(),
        .init_calib_complete(ddr3_phy_init_done),
        // input
        .sys_rst(sys_rst)
);
assign ddr3_addr[15:14] = 0;
`else
sim_mig u_sim_mig(
  .i_clk(sys_clk),
  .i_rst(sys_rst),
  .i_app_addr(mig_app_addr),
  .i_app_cmd(mig_app_cmd),
  .i_app_en(mig_app_en),
  .o_app_rdy(mig_app_rdy),
  .o_app_rd_data(mig_app_rd_data),
  .o_app_rd_data_valid(mig_app_rd_data_valid),
  .i_app_wdf_data(mig_app_wdf_data),
  .i_app_wdf_end(mig_app_wdf_end),
  .i_app_wdf_mask(mig_app_wdf_mask),
  .o_app_wdf_rdy(mig_app_wdf_rdy),
  .i_app_wdf_wren(mig_app_wdf_wren)
);
assign ddr3_addr    = 0;
assign ddr3_ba      = 0;
assign ddr3_cke     = 0;
assign ddr3_dm      = 0;
assign ddr3_odt     = 0;
assign ddr3_cas_n   = 1'b1;
assign ddr3_ras_n   = 1'b1;
assign ddr3_reset_n = 1'b1;
assign ddr3_cs_n    = 1'b1;
assign ddr3_we_n    = 1'b1;
`OBUFDS u_OBUFDS_ddr3_ck(.I(1'b0), .O(ddr3_ck_p), .OB(ddr3_ck_n));
`IOBUFDS u_IOBUFDS_ddr3_dqs_0(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[0]), .IOB(ddr3_dqs_n[0]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_1(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[1]), .IOB(ddr3_dqs_n[1]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_2(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[2]), .IOB(ddr3_dqs_n[2]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_3(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[3]), .IOB(ddr3_dqs_n[3]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_4(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[4]), .IOB(ddr3_dqs_n[4]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_5(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[5]), .IOB(ddr3_dqs_n[5]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_6(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[6]), .IOB(ddr3_dqs_n[6]), .O());
`IOBUFDS u_IOBUFDS_ddr3_dqs_7(.I(1'b0), .T(1'b1), .IO(ddr3_dqs_p[7]), .IOB(ddr3_dqs_n[7]), .O());
`endif

/*
wire heartbeat;
heartbeat u_heartbeat(
        .i_clk(sys_clk),
        .i_rst(sys_rst),
        .o_heartbeat(heartbeat)
);

assign	gpio_led_0 = heartbeat;
assign	gpio_led_1 = ~heartbeat;
assign	gpio_led_2 = heartbeat;
assign	gpio_led_3 = ~heartbeat;
assign	gpio_led_4 = heartbeat;
assign	gpio_led_5 = ~heartbeat;
assign	gpio_led_6 = heartbeat;
assign	gpio_led_7 = ~heartbeat;
*/

system_core #(.UART_CLK_DIV(UART_CLK_DIV)) u_system_core(
  .i_clk(sys_clk),
  .i_rst(sys_rst | cpu_reset),
  .o_uart_cts(uart_cts),
  .i_uart_rts(uart_rts),
  .o_uart_rx(uart_rx),
  .i_uart_tx(uart_tx),
  .i_gpio_sw(4'b0),
  .o_ddr3_instr_read(instr_read),
  .o_ddr3_instr_addr(instr_addr),
  .i_ddr3_instr_ack(instr_ack),
  .i_ddr3_instr_data(instr_data),
  .o_ddr3_wb_cyc(ddr3_i_wb_cyc),
  .o_ddr3_wb_stb(ddr3_i_wb_stb),
  .o_ddr3_wb_we (ddr3_i_wb_we ),
  .o_ddr3_wb_cti(ddr3_i_wb_cti),
  .o_ddr3_wb_bte(ddr3_i_wb_bte),
  .o_ddr3_wb_adr(ddr3_i_wb_adr),
  .o_ddr3_wb_sel(ddr3_i_wb_sel),
  .o_ddr3_wb_dat(ddr3_i_wb_dat),
  .i_ddr3_wb_ack(ddr3_o_wb_ack),
  .i_ddr3_wb_dat(ddr3_o_wb_dat)
);

endmodule
